package javax.slee.management;

import javax.management.ObjectName;
import javax.slee.SbbID;
import javax.slee.ServiceID;

/**
 * This class identifies a notification such as an {@link AlarmNotification alarm} or
 * {@link TraceNotification trace} notification as being generated in response to
 * some action performed by an SBB.  For example, if an SBB raises an alarm using the
 * {@link javax.slee.facilities.AlarmFacility alarm facility}, an alarm notification
 * will be generated containing an <code>SbbNotification</code> object that identifies
 * the SBB that raised the alarm.
 * @since SLEE 1.1
 */
public final class SbbNotification extends AbstractNotificationSource implements NotificationSource {
    /**
     * The JMX notification type of alarm notifications that are generated in response
     * to an SBB interacting with the {@link javax.slee.facilities.AlarmFacility}.
     * <p>
     * The notification type is equal to the string "javax.slee.management.alarm.sbb".
     */
    public static final String ALARM_NOTIFICATION_TYPE = "javax.slee.management.alarm.sbb";

    /**
     * The JMX notification type of trace notifications that are generated in response
     * to an SBB interacting with the Trace Facility via a {@link javax.slee.facilities.Tracer}
     * object.
     * <p>
     * The notification type is equal to the string "javax.slee.management.trace.sbb".
     */
    public static final String TRACE_NOTIFICATION_TYPE = "javax.slee.management.trace.sbb";

    /**
     * The JMX notification type of usage notifications that are generated by a
     * {@link javax.slee.usage.UsageMBean} containing an <code>SbbNotification</code> as
     * a notification source.
     * <p>
     * The notification type is equal to the string "javax.slee.management.usage.sbb".
     */
    public static final String USAGE_NOTIFICATION_TYPE = "javax.slee.management.usage.sbb";

    /**
     * The JMX Object Name property key that identifies the name of the service in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "serviceName".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_VENDOR_KEY
     * @see #SERVICE_VERSION_KEY
     * @see #SBB_NAME_KEY
     * @see #SBB_VENDOR_KEY
     * @see #SBB_VERSION_KEY
     * @since SLEE 1.1
     */
    public static final String SERVICE_NAME_KEY = "serviceName";

    /**
     * The JMX Object Name property key that identifies the vendor of the service in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "serviceVendor".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_NAME_KEY
     * @see #SERVICE_VERSION_KEY
     * @see #SBB_NAME_KEY
     * @see #SBB_VENDOR_KEY
     * @see #SBB_VERSION_KEY
     * @since SLEE 1.1
     */
    public static final String SERVICE_VENDOR_KEY = "serviceVendor";

    /**
     * The JMX Object Name property key that identifies the version of the service in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "serviceVersion".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_NAME_KEY
     * @see #SERVICE_VENDOR_KEY
     * @see #SBB_NAME_KEY
     * @see #SBB_VENDOR_KEY
     * @see #SBB_VERSION_KEY
     * @since SLEE 1.1
     */
    public static final String SERVICE_VERSION_KEY = "serviceVersion";

    /**
     * The JMX Object Name property key that identifies the name of the SBB in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "sbbName".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_NAME_KEY
     * @see #SERVICE_VENDOR_KEY
     * @see #SERVICE_VERSION_KEY
     * @see #SBB_VENDOR_KEY
     * @see #SBB_VERSION_KEY
     * @since SLEE 1.1
     */
    public static final String SBB_NAME_KEY = "sbbName";

    /**
     * The JMX Object Name property key that identifies the vendor of the SBB in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "sbbVendor".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_NAME_KEY
     * @see #SERVICE_VENDOR_KEY
     * @see #SERVICE_VERSION_KEY
     * @see #SBB_NAME_KEY
     * @see #SBB_VERSION_KEY
     * @since SLEE 1.1
     */
    public static final String SBB_VENDOR_KEY = "sbbVendor";

    /**
     * The JMX Object Name property key that identifies the version of the SBB in a
     * Usage MBean whose {@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY}
     * property has a value equal to {@link #USAGE_NOTIFICATION_TYPE}.  This key is
     * equal to the string "sbbVersion".
     * @see javax.slee.usage.UsageMBean#BASE_OBJECT_NAME
     * @see javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY
     * @see #SERVICE_NAME_KEY
     * @see #SERVICE_VENDOR_KEY
     * @see #SERVICE_VERSION_KEY
     * @see #SBB_NAME_KEY
     * @see #SBB_VENDOR_KEY
     * @since SLEE 1.1
     */
    public static final String SBB_VERSION_KEY = "sbbVersion";


    /**
     * Create a new <code>SbbNotification</code> object that uniquely identifies an SBB
     * component within a service.
     * @param service the component identifier of the service.
     * @param sbb the component identifier of the SBB.
     * @throws NullPointerException if either argument is <code>null</code>.
     */
    public SbbNotification(ServiceID service, SbbID sbb) {
        if (service == null) throw new NullPointerException("service is null");
        if (sbb == null) throw new NullPointerException("sbb is null");
        this.service = service;
        this.sbb = sbb;
    }

    /**
     * Get the service component identifier of this notification source.
     * @return the service component identifier.
     */
    public ServiceID getService() {
        return service;
    }

    /**
     * Get the SBB component identifier of this notification source.
     * @return the SBB component identifier.
     */
    public SbbID getSbb() {
        return sbb;
    }

    /**
     * Get the JMX notification type of alarm notifications generated in response
     * to an SBB interacting with the Alarm Facility.
     * @return the string defined by {@link #ALARM_NOTIFICATION_TYPE}.
     */
    public String getAlarmNotificationType() {
        return ALARM_NOTIFICATION_TYPE;
    }

    /**
     * Get the JMX notification type of trace notifications generated in response
     * to an SBB interacting with the Trace Facility.
     * @return the string defined by {@link #TRACE_NOTIFICATION_TYPE}.
     */
    public String getTraceNotificationType() {
        return TRACE_NOTIFICATION_TYPE;
    }

    /**
     * Get the JMX notification type of usage notifications generated in response
     * to an SBB interacting with its usage parameters.
     * @return the string defined by {@link #USAGE_NOTIFICATION_TYPE}.
     */
    public String getUsageNotificationType() {
        return USAGE_NOTIFICATION_TYPE;
    }

    /**
     * Get a JMX Object Name property string that uniquely identifies the specified
     * service and SBB, suitable for inclusion in the Object Name of a Usage MBean.
     * This method makes use of the {@link ObjectName#quote}</code> method to ensure
     * that the component identifier attributes are valid for inclusion as property
     * values in an Object Name.
     * <p>
     * This method can be used as follows to manually construct a complete Object Name
     * for a Usage MBean:
     * <br><ul><code>
     *     ObjectName name = new ObjectName(<br>
     *     &nbsp;&nbsp;&nbsp;&nbsp;{@link javax.slee.usage.UsageMBean#BASE_OBJECT_NAME} + "," +<br>
     *     &nbsp;&nbsp;&nbsp;&nbsp;{@link javax.slee.usage.UsageMBean#USAGE_PARAMETER_SET_NAME_KEY} + "=" + ObjectName.quote(paramSetName) + "," &nbsp; // optional<br>
     *     &nbsp;&nbsp;&nbsp;&nbsp;{@link javax.slee.usage.UsageMBean#NOTIFICATION_SOURCE_KEY} + "=" + {@link #USAGE_NOTIFICATION_TYPE SbbNotification.USAGE_NOTIFICATION_TYPE} + "," +<br>
     *     &nbsp;&nbsp;&nbsp;&nbsp;{@link #getUsageMBeanProperties(ServiceID, SbbID) SbbNotification.getUsageMBeanProperties(service, sbb)}<br>
     *     );
     * </code></ul>
     * @param service the component identifier of the service.
     * @param sbb the component identifier of the SBB.
     * @return an Object Name property string that uniquely identifies the specified
     *        service and SBB components.
     * @throws NullPointerException if either argument is <code>null</code>.
     */
    public static String getUsageMBeanProperties(ServiceID service, SbbID sbb) {
        if (service == null) throw new NullPointerException("service is null");
        if (sbb == null) throw new NullPointerException("sbb is null");
        StringBuffer buf = new StringBuffer();
        buf.append(SERVICE_NAME_KEY).append('=').append(ObjectName.quote(service.getName())).append(',');
        buf.append(SERVICE_VENDOR_KEY).append('=').append(ObjectName.quote(service.getVendor())).append(',');
        buf.append(SERVICE_VERSION_KEY).append('=').append(ObjectName.quote(service.getVersion())).append(',');
        buf.append(SBB_NAME_KEY).append('=').append(ObjectName.quote(sbb.getName())).append(',');
        buf.append(SBB_VENDOR_KEY).append('=').append(ObjectName.quote(sbb.getVendor())).append(',');
        buf.append(SBB_VERSION_KEY).append('=').append(ObjectName.quote(sbb.getVersion()));
        return buf.toString();
    }

    /**
     * Get a JMX Object Name property string that uniquely identifies the service
     * and SBB of this notification source, suitable for inclusion in the Object Name
     * of a Usage MBean.
     * <p>
     * This method is equivalent to {@link #getUsageMBeanProperties(ServiceID, SbbID)
     * getUsageMBeanProperties(getService(), getSbb())}.
     * @return an Object Name property string that uniquely identifies the service
     *        and SBB components of this notification source.
     */
    public String getUsageMBeanProperties() {
        return getUsageMBeanProperties(service, sbb);
    }

    /**
     * Compare this notification source for equality with another object.
     * @param obj the object to compare this with.
     * @return <code>true</code> if <code>obj</code> is an instance of this class and
     *        references the same SBB and service component identifiers as this,
     *        <code>false</code> otherwise.
     */
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof SbbNotification)) return false;

        SbbNotification that = (SbbNotification)obj;
        return this.service.equals(that.service)
            && this.sbb.equals(that.sbb);
    }

    /**
     * Get a hash code value for this notification source.
     * @return a hash code value for this notification source.
     */
    public int hashCode() {
        return service.hashCode() ^ sbb.hashCode();
    }

    /**
     * Get a string representation for this notification source.
     * @return a string representation for this notification source.
     * @see Object#toString()
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("SbbNotification[service=").append(service).
            append(",sbb=").append(sbb).
            append(']');
        return buf.toString();
    }

    /**
     * Compare this notification source with the specified object for order.
     * Returns a negative integer, zero, or a positive integer if this object
     * is less than, equal to, or greater than the specified object.
     * <p>
     * If <code>obj</code> is an <code>SbbNotification</code>, order is determined
     * by comparing first the encapsulated service component identifier and then,
     * if the service component identifiers are equal, the encapsulated SBB
     * component identifier.  Otherwise, if <code>obj</code> is a
     * <code>NotificationSource</code>, ordering is determined by comparing the
     * class name of this class with the class name of <code>obj</code>.
     * @param obj the object to compare this with.
     * @return a negative integer, zero, or a positive integer if this notification
     *        source is considered less than, equal to, or greater than the
     *        specified object.
     * @throws ClassCastException if <code>obj</code> does not implement the
     *        {@link NotificationSource} interface.
     * @see Comparable#compareTo(Object)
     */
    public int compareTo(Object obj) {
        // can't compare with null
        if (obj == null) throw new NullPointerException("obj is null");
        if (obj == this) return 0;

        if (obj instanceof SbbNotification) {
            // compare the service id then the sbb id
            SbbNotification that = (SbbNotification)obj;
            int serviceComparison = this.service.compareTo(that.service);
            return serviceComparison != 0 ? serviceComparison : this.sbb.compareTo(that.sbb);
        }
        else {
            return super.compareTo(TYPE, obj);
        }
    }


    // protected

    protected String getClassName() {
        return TYPE;
    }


    private final ServiceID service;
    private final SbbID sbb;

    // constant to avoid expensive getClass() invocations at runtime
    private static final String TYPE = SbbNotification.class.getName();
}
